# !/usr/bin/env python
# -*- coding: utf-8 -*-

"""
__project__ =  'tricker'
__file__    =  'worker.py'
__author__  =  'king'
__time__    =  '2024/5/22 14:17'


                              _ooOoo_
                             o8888888o
                             88" . "88
                             (| -_- |)
                             O\  =  /O
                          ____/`---'\____
                        .'  \\|     |//  `.
                       /  \\|||  :  |||//  \
                      /  _||||| -:- |||||-  \
                      |   | \\\  -  /// |   |
                      | \_|  ''\---/''  |   |
                      \  .-\__  `-`  ___/-. /
                    ___`. .'  /--.--\  `. . __
                 ."" '<  `.___\_<|>_/___.'  >'"".
                | | :  `- \`.;`\ _ /`;.`/ - ` : | |
                \  \ `-.   \_ __\ /__ _/   .-` /  /
           ======`-.____`-.___\_____/___.-`____.-'======
                              `=---='
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                       佛祖保佑        永无BUG
"""
import json
import os
from pathlib import Path
import shutil
import tempfile

import jinja2
import yaml
from typing import Optional

from tricker.commands.tacker.models.vnf import (
    CASR,
    Entity,
)
from tricker.commands.tacker.models.tacker import (
    InstantiationState,
    OnboardingState,
    OperationalState,
)
from tricker.common import utils
from tricker.common.client import OpenStackClient
from tricker.common.log import LOG


def get_abspath() -> str:
    path = os.path.abspath(__file__)
    path = os.path.dirname(path)
    return path


def get_templates_path() -> str:
    return f'{get_abspath()}/templates'


def get_csar_path() -> str:
    return f'{get_abspath()}/files/csar/'


def get_definitions_abspath() -> str:
    return f'{get_abspath()}/files/csar/Definitions/'


def get_userdata_abspath() -> str:
    return f'{get_abspath()}/files/csar/UserData/'


def get_csar_file(with_extension: bool = True) -> str:
    if with_extension:
        return f'{get_abspath()}/files/sample_vnf_package_csar.zip'
    return f'{get_abspath()}/files/sample_vnf_package_csar'


def get_vnfd_file() -> str:
    return f'{get_abspath()}/files/csar/Definitions/sample_vnfd_types.yaml'


def get_request_file() -> str:
    return f'{get_abspath()}/files/sample_request.json'


class Worker(object):
    def __init__(self):
        self.client = OpenStackClient()

    def create_vim(self):
        vim = self.client.get_vim()
        if vim:
            LOG.info('default vim exists, skip creating')
        else:
            LOG.info('start creating default vim')
            _ = self.client.create_vim()
            LOG.info('created default vim success')

    @staticmethod
    def create_csar():
        csar_file = get_csar_file()
        casr = get_csar_file(False)
        root_dir = get_csar_path()
        if os.path.isfile(csar_file):
            LOG.info('remove old CSAR')
            os.remove(csar_file)
        shutil.make_archive(
            casr,
            'zip',
            root_dir=root_dir,
            logger=LOG,
        )
        return csar_file

    def check_vnfd_id(self) -> (bool, Optional[str]):
        vnfd_id = None
        with open(get_vnfd_file(), 'r') as f:
            data = yaml.safe_load(f)
            resources = data.get('node_types')
            if isinstance(resources, dict):
                for key, value in resources.items():
                    vnfd_id = (
                        value.get('properties', {})
                        .get('descriptor_id', {})
                        .get('default', None)
                    )
        if vnfd_id:
            packages = self.client.get_packages(vnfdId=vnfd_id)
            for package in packages:
                old_vnfd_id = package.get('vnfdId', None)
                if vnfd_id == old_vnfd_id:
                    LOG.error(f'duplicate vnfd id {vnfd_id}')
                    return False, None
        else:
            LOG.error('emtpy vnfd id')
            return False, None
        return True, vnfd_id

    @staticmethod
    def get_instantiate_data(request_json: str = None) -> dict:
        if not request_json:
            request_json = get_request_file()
        if os.path.isfile(request_json):
            with open(request_json, 'r') as f:
                data = json.load(f)
                return data
        else:
            return {
                "flavourId": "simple",
                "additionalParams": {
                    "lcm-operation-user-data": "./UserData/lcm_user_data.py",
                    "lcm-operation-user-data-class": "StandardUserData",
                },
            }

    def run(self, csar: str, request_json: str) -> int:
        self.create_vim()
        package = self.client.create_package()
        if not csar:
            csar = self.create_csar()
            result, _ = self.check_vnfd_id()
            if not result:
                return 1
        package_id = package.get('id')
        self.client.upload_package(package_id, csar)
        utils.wait_resource_become(
            self.client.get_package_onboarding_state,
            OnboardingState.ONBOARDED,
            package_id=package_id,
        )
        package = self.client.get_package(package_id)
        LOG.info(package)
        vnfd_id = package.get('vnfdId')
        vnf_instance = self.client.create_vnf_instance(vnfd_id)
        data = self.get_instantiate_data(request_json)
        self.client.instantiate(vnf_instance['id'], data)

    def delete_vnf_instances(self):
        instances = self.client.get_vnf_instances()
        for instance in instances:
            instance_id = instance['id']
            if (
                instance.get('instantiationState')
                == InstantiationState.INSTANTIATED
            ):
                self.client.terminate(instance_id)
                utils.wait_resource_become(
                    self.client.get_vnf_instance_instantiation_state,
                    InstantiationState.NOT_INSTANTIATED,
                    vnf_instance_id=instance_id,
                )
            self.client.delete_vnf_instance(instance_id)

    def delete_vnf_packages(self):
        packages = self.client.get_packages()
        for package in packages:
            if (
                package.get('operationalState', None)
                == OperationalState.ENABLED
            ):
                self.client.update_package(package['id'])

            self.client.delete_package(package['id'])

    def run_clean(self):
        self.delete_vnf_instances()
        self.delete_vnf_packages()

    @staticmethod
    def maker_casr(entity: Entity, dev: bool) -> None:
        LOG.info(f'create CASR with {entity}')
        template_path = get_templates_path()
        with tempfile.TemporaryDirectory() as temp_dir:
            LOG.info('create temporary directory %s', temp_dir)
            if dev:
                temp_dir = CASR.DEV_PATH
                if os.path.isdir(temp_dir):
                    shutil.rmtree(temp_dir, ignore_errors=True)
            for _path in CASR.ALL:
                if _path == CASR.BaseHOT:
                    _path = _path % {'flavour_id': entity.VNF.flavour_id}
                    CASR.BaseHOT = _path
                if _path == CASR.BaseHOT_Nested:
                    _path = _path % {'flavour_id': entity.VNF.flavour_id}
                    CASR.BaseHOT_Nested = _path
                path = os.path.join(temp_dir, _path)
                LOG.info(f'mkdir {path}')
                Path(path).mkdir(parents=True, exist_ok=True)
            for template in os.listdir(template_path):
                if not template.endswith('j2'):
                    continue
                template_name = os.path.splitext(template)[0]
                with open(os.path.join(template_path, template), 'r') as f:
                    data = f.read()
                    template = jinja2.Template(data).render(
                        image=entity.image,
                        flavor=entity.flavor,
                        network=entity.network,
                        server=entity.server,
                        VNF=entity.VNF,
                    )
                    dest = CASR.file_mappings().get(template_name)
                    if not dest:
                        LOG.error(
                            f'{template_name} not found in '
                            f'CASR.file_mappings()'
                        )
                        continue
                    with open(os.path.join(temp_dir, dest), 'w+') as f:
                        f.writelines(template)
            for f in CASR.sol001_files():
                shutil.copy(
                    os.path.join(get_definitions_abspath(), f),
                    os.path.join(temp_dir, CASR.Definitions),
                )
            for f in CASR.userdata_files():
                shutil.copy(
                    os.path.join(get_userdata_abspath(), f),
                    os.path.join(temp_dir, CASR.UserData),
                )
            shutil.make_archive('csar', 'zip', root_dir=temp_dir, logger=LOG)
            LOG.info('create csar.zip success')
            shutil.move(os.path.join(temp_dir, 'request.json'), 'request.json')
            LOG.info('create request.json success')
